package com.zhouxiaoge.gitlab.commont;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.zhouxiaoge.gitlab.entity.Jobs;
import com.zhouxiaoge.gitlab.entity.Pipeline;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

/**
 * @author 周明阳
 * @date 2019/7/29 09:39
 */

@Component
public class GitlabUtil {
    private static final String gitlabName = "root";
    private static final String gitlabPassword = "root";

    private static final String GITLAB_URL = "http://localhost:8888/api/v4/";
    private static final String PRIVATE_TOKEN = "5-thhCxoChaxwdsGzCNa";

    /**
     * 根据项目名称创建一个pipelin
     *
     * @param projectName 项目名称
     * @param ref         项目分支名称
     * @return 流水线pipeline信息
     */
    public Pipeline cratePipelineForProjectName(String projectName, String ref) {
        if (StringUtils.isNotBlank(projectName)) {
            String projectId = getProjectId(projectName);
            if (StringUtils.isNotBlank(projectId)) {
                String requestGitlabUrl = "/projects/" + projectId + "/pipeline?ref=" + ref;
                String creatPipelineResult = requestGitlab(requestGitlabUrl, "POST");
                return JSONObject.parseObject(creatPipelineResult, Pipeline.class);
            }
        }
        return null;
    }

    /**
     * 根据项目名称重试最新 pipeline
     *
     * @param projectName 项目名称
     * @return Jobs信息
     */
    public Jobs retryJobsPipeline(String projectName) {
        String projectId = getProjectId(projectName);
        String pipelineId = getPipelineId(projectName);
        if (StringUtils.isNotBlank(projectId) && StringUtils.isNotBlank(pipelineId)) {
            String retryJobsPipelineUrl = "/projects/" + projectId + "/pipelines/" + pipelineId + "/retry";
            String retryJobsPipelineResult = requestGitlab(retryJobsPipelineUrl, "POST");
            return JSONObject.parseObject(retryJobsPipelineResult, Jobs.class);
        }
        return null;
    }

    /**
     * 根据projectName 执行流水线的各个job 只用于手动触发
     *
     * @param projectName 项目名称
     */
    @Async
    public void playProjectPipeline(String projectName) {
        String projectId = getProjectId(projectName);
        String pipelineId = getPipelineId(projectName);
        if (StringUtils.isNotBlank(projectId) && StringUtils.isNotBlank(pipelineId)) {
            String url = "/projects/" + projectId + "/pipelines/" + pipelineId + "/jobs";
            String queryPipelineJobs = requestGitlab(url, "GET");
            List<Jobs> jobs = Objects.requireNonNull(JSONArray.parseArray(queryPipelineJobs)).toJavaList(Jobs.class);
            for (Jobs job : jobs) {
                Jobs jobs1 = playJob(projectId, job.getId());
                while (!"success".equalsIgnoreCase(jobs1.getStatus())) {
                    Jobs singleJob = getSingleJob(projectId, job.getId());
                    if ("success".equalsIgnoreCase(singleJob.getStatus())) {
                        break;
                    }
                }
            }
        }
    }

    public String getProjectId(String projectName) {
        String projects = requestGitlab("projects", "get");
        System.out.println(projects);
        List<Jobs> projectList = Objects.requireNonNull(JSONArray.parseArray(projects)).toJavaList(Jobs.class);
        Optional<Jobs> jobsOptional = projectList.stream().filter(job -> StringUtils.equals(job.getName(), projectName)).findFirst();
        return jobsOptional.map(Jobs::getId).orElse(null);
    }

    public String getPipelineId(String projectName) {
        String projectId = getProjectId(projectName);
        if (StringUtils.isNotBlank(projectId)) {
            List<Pipeline> pipelines = queryProjectPipelines(projectId);
            Optional<Pipeline> optionalPipeline = pipelines.stream().findFirst();
            return optionalPipeline.map(Pipeline::getId).orElse(null);
        }
        return null;
    }

    /**
     * 获取用户的所有项目信息
     *
     * @return 所有项目信息 List<Jobs
     */
    public List<Jobs> queryAllProjectInfo() {
        String get = requestGitlab("projects", "get");
        return Objects.requireNonNull(JSONArray.parseArray(get)).toJavaList(Jobs.class);
    }

    /**
     * 根据项目id获取该项目的所有pipeline
     *
     * @param projectId 项目id
     */
    public List<Pipeline> queryProjectPipelines(String projectId) {
        String url = "/projects/" + projectId + "/pipelines";
        String get = requestGitlab(url, "GET");
        return Objects.requireNonNull(JSONArray.parseArray(get)).toJavaList(Pipeline.class);
    }

    /**
     * 在项目下新创建pipeline 注：要创建pipeline，该项目必须已有pipeline
     *
     * @param projectId 项目id
     * @param ref       ref 注：该值应该为代码分支名称，如：master
     * @return pipeline信息
     */
    public Pipeline createNewPipeline(String projectId, String ref) {
        String url = "/projects/" + projectId + "/pipeline?ref=" + ref;
        String get = requestGitlab(url, "POST");
        return JSONObject.parseObject(get, Pipeline.class);
    }

    /**
     * 根据projectId和pipelineId获取pipeline信息
     *
     * @param projectId  项目ID
     * @param pipelineId 流水线ID
     */
    public static Pipeline getSinglePipeline(String projectId, String pipelineId) {
        String url = "/projects/" + projectId + "/pipelines/" + pipelineId;
        String get = requestGitlab(url, "GET");
        return JSONObject.parseObject(get, Pipeline.class);
    }

    /**
     * 根据projectId和pipelineId获取pipeline的所有jobs信息
     *
     * @param projectId  项目id
     * @param pipelineId 流水线id
     * @return 作业信息集合
     */
    public List<Jobs> listPipelineJobs(String projectId, String pipelineId) {
        String url = "/projects/" + projectId + "/pipelines/" + pipelineId + "/jobs";
        String get = requestGitlab(url, "GET");
        return Objects.requireNonNull(JSONArray.parseArray(get)).toJavaList(Jobs.class);
    }

    /**
     * 根据projectId和JobId 执行job
     *
     * @param projectId 项目id
     * @param jobsId    作业id
     * @return 执行job后job信息
     */
    public Jobs playJob(String projectId, String jobsId) {
        String url = "/projects/" + projectId + "/jobs/" + jobsId + "/play";
        String get = requestGitlab(url, "POST");
        return JSONObject.parseObject(get, Jobs.class);
    }

    /**
     * 获取单个job信息
     *
     * @param projectId 项目id
     * @param jobsId    作业id
     * @return 作业信息
     */
    public Jobs getSingleJob(String projectId, String jobsId) {
        String url = "/projects/" + projectId + "/jobs/" + jobsId;
        String get = requestGitlab(url, "GET");
        return JSONObject.parseObject(get, Jobs.class);
    }

    /**
     * 重试一个job
     *
     * @param projectId 项目id
     * @param jobsId    作业id
     */
    public void retryJob(String projectId, String jobsId) {
        String url = "/projects/" + projectId + "/jobs/" + jobsId + "/retry";
        String get = requestGitlab(url, "POST");
        System.out.println(get);
    }

    /**
     * 修改.gitlab-ci.yml文件
     *
     * @param content               .gitlab-ci.yml内容
     * @param applicationGitlabPath 项目在gitlab上的地址 比如：http://localhost/root/gitlabYmlTest.git"
     */
    @Async
    public String updateGitlabYml(String content, String applicationGitlabPath) {
        if (StringUtils.isBlank(applicationGitlabPath)) {
            return "项目地址不能为空";
        }
        String gitlabUrl = applicationGitlabPath.substring(0, applicationGitlabPath.lastIndexOf("."));

        UsernamePasswordCredentialsProvider provider = new UsernamePasswordCredentialsProvider(gitlabName, gitlabPassword);
        String path = UUID.randomUUID().toString().replace("-", "");
        File file = new File("test//" + path + "//");
        try {
            Git.cloneRepository().setURI(gitlabUrl).setDirectory(file).setCredentialsProvider(provider).call();
            createYml(content, "test//" + path + "//.gitlab-ci.yml");
            Git.open(file).add().addFilepattern(".").call();
            Git.open(file).commit().setAll(true).setMessage("修改.gitlab-ci.yml " + LocalDateTime.now().toString()).call();
            Git.open(file).push().setPushAll().setForce(true).setRemote(gitlabUrl).setCredentialsProvider(provider).call();
            delFolder("test");
        } catch (GitAPIException | IOException e) {
            e.printStackTrace();
            return "修改.gitlab-ci.yml异常";
        }
        return "SUCCESS";
    }

    /**
     * 删除文件夹
     */
    private static void delFolder(String folderPath) {
        try {
            delAllFile(folderPath); //删除完里面所有内容
            String filePath = folderPath;
            filePath = filePath.toString();
            File myFilePath = new File(filePath);
            myFilePath.delete(); //删除空文件夹
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除指定文件夹下所有文件
     */
    private static boolean delAllFile(String path) {
        boolean flag = false;
        File file = new File(path);
        if (!file.exists()) {
            return flag;
        }
        if (!file.isDirectory()) {
            return flag;
        }
        String[] tempList = file.list();
        File temp = null;
        for (int i = 0; i < tempList.length; i++) {
            if (path.endsWith(File.separator)) {
                temp = new File(path + tempList[i]);
            } else {
                temp = new File(path + File.separator + tempList[i]);
            }
            if (temp.isFile()) {
                temp.delete();
            }
            if (temp.isDirectory()) {
                delAllFile(path + "/" + tempList[i]);//先删除文件夹里面的文件
                delFolder(path + "/" + tempList[i]);//再删除空文件夹
                flag = true;
            }
        }
        return flag;
    }

    private static void createYml(String content, String filePath) {
        FileWriter fileWriter = null;
        try {
            fileWriter = new FileWriter(filePath, false);
            fileWriter.write(content);
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            try {
                fileWriter.flush();
                fileWriter.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    private static String requestGitlab(String path, String methodType) {
        try {
            String url = GITLAB_URL + path;
            System.out.println("==== " + url + " ====");
            CloseableHttpClient aDefault = HttpClients.createDefault();
            if ("post".equalsIgnoreCase(methodType)) {
                HttpPost httpPost = new HttpPost(url);
                httpPost.setHeader("PRIVATE-TOKEN", PRIVATE_TOKEN);
                CloseableHttpResponse execute = aDefault.execute(httpPost);
                HttpEntity entity = execute.getEntity();
                return EntityUtils.toString(entity);
            } else if ("get".equalsIgnoreCase(methodType)) {
                HttpGet httpGet = new HttpGet(url);
                httpGet.setHeader("PRIVATE-TOKEN", PRIVATE_TOKEN);
                CloseableHttpResponse execute = aDefault.execute(httpGet);
                HttpEntity entity = execute.getEntity();
                return EntityUtils.toString(entity);
            } else if ("delete".equalsIgnoreCase(methodType)) {
                HttpDelete httpDelete = new HttpDelete(url);
                httpDelete.setHeader("PRIVATE-TOKEN", PRIVATE_TOKEN);
                CloseableHttpResponse execute = aDefault.execute(httpDelete);
                HttpEntity entity = execute.getEntity();
                return EntityUtils.toString(entity);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return null;
    }
}
